package com.arpa.wms.controller;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.core.web.MediaTypes;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.Page;
import com.arpa.ntocc.common.common.constant.StrConsts;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.ShipmentWarehouseValidate;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.security.annotation.NeedLogin;
import com.arpa.wms.domain.consts.CommonConst;
import com.arpa.wms.domain.consts.ResultDO;
import com.arpa.wms.domain.dto.AccountBalanceDTO;
import com.arpa.wms.domain.dto.DeliveryDistributionDTO;
import com.arpa.wms.domain.dto.OutboundDTO;
import com.arpa.wms.domain.entity.Outbound;
import com.arpa.wms.domain.enums.OutboundStatusEnum;
import com.arpa.wms.domain.vo.DeliveryDistributionVO;
import com.arpa.wms.domain.vo.DeliveryVO;
import com.arpa.wms.domain.vo.OutboundVO;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.swagger.annotations.*;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;


//import com.arpa.wms.mq.provider.WayBillSender;


/**
 * <p>
 * 出库单 Controller
 * </p>
 *
 * @author LEO
 * @since 2020-08-07
 */
@Api(tags = "出库单")
@RestController
@RequestMapping(value = "/outbound", produces = "application/json;charset=UTF-8")
@Log4j2
public class OutboundController {

    private final IOutboundService iOutboundService;

    @Resource
    public RedissonClient redissonClient;


    @Autowired
    private DeliveryCancelHandleService deliveryCancelHandleService;

    //private final WayBillSender wayBillSender;

    @Autowired
    private OutCheckService outCheckService;

    @Autowired
    private ReCheckService reCheckService;

    @Autowired
    private DeliveryReviewService deliveryReviewService;

    public OutboundController(IOutboundService iOutboundService
            //, IDeliveryService iDeliveryService
            //, WayBillSender wayBillSender
    ) {
        this.iOutboundService = iOutboundService;
        //this.wayBillSender = wayBillSender;
    }

    /**
     * 获取 出库单 列表
     *
     * @param outboundDTO
     * @param request
     * @return
     */
    @ApiOperation(value = "获取 出库单 列表", notes = "获取 出库单 列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @ApiResponses(@ApiResponse(code = 0, message = "success", response = OutboundVO.class))
    @NeedLogin
    @GetMapping()
    public Result list(OutboundDTO outboundDTO, HttpServletRequest request) {
        try {
            //校验授权仓库code和货主code不能为空
             Map<String, List<String>> authMap = ShipmentWarehouseValidate.validateShipmentCodesAndWarehouseCodes();
            //登录账号所在组织机构过滤
            outboundDTO.setGroupCode(UserUtil.getBranchCode());
            //登录账号授权的仓库过滤
            outboundDTO.setWarehouseCodes(authMap.get(CommonConst.WAREHOUSE_CODES));
            //登录账号授权的货主过滤
            outboundDTO.setShipmentCodes(authMap.get(CommonConst.SHIPMENT_CODES));

            OutboundVO outboundVOSum = iOutboundService.queryListTotal(outboundDTO);
            List<OutboundVO> outboundVOList = iOutboundService.queryList(outboundDTO);

            Page returnPage = new Page(outboundDTO.getPageNum(), outboundDTO.getPageSize());
            returnPage.setRecords(outboundVOList);
            returnPage.setTotal(outboundVOSum.getTotal());
            returnPage.setSumObject(outboundVOSum);
            return Result.ok().data(returnPage);
        } catch (Exception e) {
            log.error(StrConsts.CATCH_MESSAGE, e);
            return Result.error(e.getMessage());
        }
    }

    /**
     * 添加 出库单
     *
     * @param entity  Outbound
     * @param request
     * @return
     */
    @ApiOperation(value = "添加 出库单 ", notes = "添加 出库单 ")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer 123456"),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @NeedLogin
    @PostMapping()
    public Result create(Outbound entity, HttpServletRequest request) {
        try {
            entity.setGroupCode(UserUtil.getBranchCode());
            iOutboundService.save(entity);
            return Result.ok("添加成功");
        } catch (Exception e) {
            log.error(StrConsts.CATCH_MESSAGE, e);
            return Result.error(e.getMessage());
        }
    }

    /**
     * 获取出库单详情
     *
     * @param code
     * @param request
     * @return
     */
    @ApiOperation(value = "获取出库单详情", notes = "获取出库单详情")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer 123456"),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000"),
            @ApiImplicitParam(name = "code", value = "出库单code", paramType = "path", dataType = "String")
    })
    @ApiResponses(@ApiResponse(code = 0, message = "success", response = OutboundVO.class))
    @NeedLogin
    @GetMapping(value = "/{code}")
    public Result outbound(@PathVariable String code, HttpServletRequest request) {
        try {
            OutboundVO vo = iOutboundService.getVOWithRegisterByCode(code);

            return Result.ok().data(vo);
        } catch (Exception e) {
            log.error(StrConsts.CATCH_MESSAGE, e);
            return Result.error(e.getMessage());
        }
    }


    /**
     * 修改出库单
     *
     * @param entity  出库单
     * @param request
     * @return
     */
    @ApiOperation(value = "修改出库单", notes = "修改出库单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @NeedLogin
    @PutMapping()
    public Result update(Outbound entity, HttpServletRequest request) {
        if (StringUtils.isBlank(entity.getCode())) {
            return Result.error("code不能为空");
        }
        int updateNum = iOutboundService.updateByCode(entity);
        if (updateNum == 0) {
            return Result.error("没有更新数据！code:" + entity.getCode());
        } else {
            return Result.ok("修改成功,更新" + updateNum + "条数据！");
        }
    }

    /**
     * 删除 出库单
     *
     * @param codes
     * @param request
     * @return
     */
    @ApiOperation(value = "删除 出库单 ", notes = "删除 出库单 ", produces = MediaTypes.JSON_UTF_8)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000"),
            @ApiImplicitParam(name = "codes", value = "codes", required = true, paramType = "query", dataType = "List")
    })
    @NeedLogin
    @DeleteMapping(value = "")
    public Result batchdel(@RequestParam List<String> codes, HttpServletRequest request) {
        try {
            iOutboundService.remove(new QueryWrapper<Outbound>().lambda().in(Outbound::getCode, codes).eq(Outbound::getGroupCode,UserUtil.getBranchCode()));
            return Result.ok();
        } catch (Exception e) {
            log.error(StrConsts.CATCH_MESSAGE, e);
            return Result.error(e.getMessage());
        }
    }




    /**
     * 复核
     *
     * @param outboundVO
     * @param request
     * @return
     */
    @ApiOperation(value = "复核出库单", notes = "复核出库单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @NeedLogin
    @PutMapping("/recheck")
    public Result recheck(@RequestBody @Validated(Outbound.Save.class) OutboundVO outboundVO, HttpServletRequest request) {

        List<String> deliveryCodes = new ArrayList<>();
        deliveryCodes.add(outboundVO.getDeliveryCode());
       /* Set<String> cancelCodes = deliveryCancelHandleService.deliveryOrderCancel(deliveryCodes);
        if(IterUtil.isNotEmpty(cancelCodes)){
            return Result.ok("订单：" + outboundVO.getDeliveryCode() + "已取消，出库单:" + outboundVO.getCode() + "取消出库");
        }*/

        return iOutboundService.PCRecheck(outboundVO);
    }


    /*@ApiOperation(value = "批量复核出库单", notes = "批量复核出库单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @NeedLogin
    @PutMapping("/batchRecheck")
    @Deprecated
    public Result batchRecheck(@RequestBody @Validated(Outbound.Save.class) OutboundVO outboundVO, HttpServletRequest request) {
        String code = outboundVO.getCode();
        outboundVO.setGroupCode(UserUtil.getBranchCode());
        RLock l  = redissonClient.getLock(getOutBatchRecheckLockKey(code));
        if(l.isLocked()){
            return Result.ok("出库单【"+code+"】正在处理中...");
        }
        deliveryReviewService.batchReview(outboundVO);

        return Result.ok("出库单【"+code+"】复核请求处理中，请稍后刷新列表。");
    }*/



    /**
     * 根据库存编码获取出货分配 列表
     *
     * @param query
     * @return
     */
    @ApiOperation(value = "根据库存编码获取出货分配 列表", notes = "根据库存编码获取出货分配 列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @ApiResponses(@ApiResponse(code = 0, message = "success", response = DeliveryDistributionVO.class))
    @NeedLogin
    @GetMapping("/queryListByInventoryCode")
    public Result queryListByInventoryCode(DeliveryDistributionDTO query) {
        try {
            List<DeliveryDistributionVO> deliveryDistributionVOList = iOutboundService.queryDistributionsByInventoryCodeAndWavePickingCode(query);
            return Result.ok().data(deliveryDistributionVOList);
        } catch (Exception e) {
            log.error(StrConsts.CATCH_MESSAGE, e);
            return Result.error(e.getMessage());
        }
    }

    /**
     * 打印面单
     *
     * @param outboundCodes
     * @param request
     * @return
     */
    @ApiOperation(value = "打印面单", notes = "打印面单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @ApiResponses(@ApiResponse(code = 0, message = "success", response = DeliveryDistributionVO.class))
    @NeedLogin
    @PostMapping("/printExpress")
    public Result printExpress(@RequestParam(name = "outboundCodes") List<String> outboundCodes, HttpServletRequest request) {
        try {
            List<DeliveryVO> deliveryVOList = iOutboundService.printExpress(outboundCodes);
            return Result.ok().data(deliveryVOList);
        } catch (Exception e) {
            log.error(StrConsts.CATCH_MESSAGE, e);
            return Result.error(e.getMessage());
        }
    }


    @ApiOperation(value = "出库确认", notes = "出库单复核完成后，进行出库确认")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer 123456"),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @NeedLogin
    @PutMapping("/check/{code}")
    public Result outboundConfirm(@PathVariable String code, HttpServletRequest request) {
            Outbound outbound = iOutboundService.getOne(new QueryWrapper<Outbound>().lambda().eq(Outbound::getCode, code).eq(Outbound::getGroupCode,UserUtil.getBranchCode()).eq(Outbound::getDeleted, "0"));
            if(outbound == null){
                return Result.error("出库单不存在，请确认。");
            }
            if(!OutboundStatusEnum.NOT_OUT.getValue().equals(outbound.getStatus())){
                return Result.error("出库单状态不正确，请确认。");
            }
           /* Set<String> cancelCodes = deliveryCancelHandleService.deliveryOrderCancel(outbound.getWavePickingCode());
            if(IterUtil.isNotEmpty(cancelCodes)){
                return Result.ok("订单：" + outbound.getDeliveryCode() + "已取消，出库单:" + outbound.getCode() + "取消出库");
            }*/
            // 更新出库信息
            return iOutboundService.outCheckAndHandelCancelDelivery(outbound);
    }

    @ApiOperation(value = "批量出库确认", notes = "批量出库确认")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer 123456"),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000"),
            @ApiImplicitParam(name = "codes", value = "出库单号集合", paramType = "body", dataType = "list", required = true)
    })
    @NeedLogin
    @PutMapping("/batchOutboundConfirm")
    public Result batchOutboundConfirm(@RequestBody List<String> codes, HttpServletRequest request) {
        if (codes.size() > 50) {
            throw new ServiceException("出库单数量不能超过50");
        }

        String groupCode = UserUtil.getBranchCode();
        List<Outbound> outbounds = iOutboundService.list(new LambdaQueryWrapper<Outbound>()
                .in(Outbound::getCode, codes)
                .eq(Outbound::getGroupCode, groupCode)
        );

        List<Outbound> allowedOutbounds = outbounds.stream()
                .filter(e -> OutboundStatusEnum.NOT_OUT.getValue().equals(e.getStatus()) && !CommonConst.STR_TWO.equals(e.getTaskStatus()))
                .collect(Collectors.toList());
        if (IterUtil.isEmpty(allowedOutbounds)) {
            throw new ServiceException("所选待出库的订单任务状态不能都是执行中");
        }
        List<String> outboundCodes = allowedOutbounds.stream().map(Outbound::getCode).collect(Collectors.toList());
        iOutboundService.update(new LambdaUpdateWrapper<Outbound>()
                .in(Outbound::getCode, outboundCodes)
                .eq(Outbound::getGroupCode, groupCode)
                .set(Outbound::getTaskStatus, CommonConst.STR_TWO)
        );
        outCheckService.batchOutboundConfirm(allowedOutbounds);
        // 更新出库信息
        return Result.ok().data("批量出库确认处理中，请稍后刷新列表。");
    }

    @ApiOperation(value = "批量复核出库确认", notes = "批量复核出库确认")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer 123456"),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000"),
            @ApiImplicitParam(name = "codes", value = "出库单号集合", paramType = "body", dataType = "list", required = true)
    })
    @NeedLogin
    @PutMapping("/batchRecheckOutboundConfirm")
    public Result batchRecheckOutboundConfirm(@RequestBody List<String> codes, HttpServletRequest request) {
        if (codes.size() > 1000) {
            throw new ServiceException("出库单数量不能超过1000");
        }

        String groupCode = UserUtil.getBranchCode();
        List<Outbound> outbounds = iOutboundService.list(new LambdaQueryWrapper<Outbound>()
                .in(Outbound::getCode, codes)
                .eq(Outbound::getGroupCode, groupCode)
        );

        List<Outbound> allowedOutbounds = outbounds.stream()
                .filter(e -> OutboundStatusEnum.NOT_RECHECK.getValue().equals(e.getStatus()) && !CommonConst.STR_TWO.equals(e.getTaskStatus()))
                .collect(Collectors.toList());
        if (IterUtil.isEmpty(allowedOutbounds)) {
            throw new ServiceException("所选待复核的订单任务状态不能都是执行中");
        }
        List<String> outboundCodes = allowedOutbounds.stream().map(Outbound::getCode).collect(Collectors.toList());
        iOutboundService.update(new LambdaUpdateWrapper<Outbound>()
                .in(Outbound::getCode, outboundCodes)
                .eq(Outbound::getGroupCode, groupCode)
                .set(Outbound::getRecheckTaskStatus, CommonConst.STR_TWO)
        );

        reCheckService.batchRecheckOutboundConfirm(outboundCodes,groupCode);
        // 更新出库信息
        return Result.ok().data("批量复核出库处理中，请稍后刷新列表。");
    }


    /**
     * 拣货确认分布式锁key
     *
     * @param code 拣货单号
     * @return
     */
    private String getOutConfirmLockKey(String code) {
        return String.format("outConfirm-lock-%s", code);
    }

    private String getOutBatchRecheckLockKey(String code) {
        return String.format("BatchRecheck-lock-%s", code);
    }
}
